package model;

import java.util.Date;
import java.util.List;
import java.util.UUID;

import common.Enums.MethodType;
import common.Exceptions.DAOException;
import common.Exceptions.FormulaException;
import common.Utils.LinkedSet;

import DAO.SessionDAO;

/**
 * This class models the 4nary association of the classes responsible for applying a value to a certain criterion.
 * For each of the elements, they are associated to a unique particular instance of each other. 
 * The only exception is the method: It can be null sometimes for this class. If it's null, 
 * means that the criterion use no method to obtain the value but applies it directly through user input.
 * The instances of this class are unique if, and only if, all of the related objects, together, are unique. 
 * @author D058585
 *
 */
public class MethodApplication 
{
	private Case caso;
	private Criterion criterion;
	private Method method;
	private Value value;
	private BusinessCase bc;
	private Date createdDate;
	
	private UUID ID;
	private UUID parentMetID;

	public MethodApplication(){}

	public BusinessCase getBc() {
		return bc;
	}
	public void setBc(BusinessCase bc) {
		this.bc = bc;
	}
	
	public Date getCreatedDate() {
		return createdDate;
	}

	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
	
	public UUID getParentMetID() {
		return parentMetID;
	}

	public void setParentMetID(UUID parentMetID) {
		this.parentMetID = parentMetID;
	}
	
	public Method getMethod() {
		return method;
	}

	public void setMethod(Method method) {
		this.method = method;
	}

	public Case getCaso() {
		return caso;
	}
	
	public void setCaso(Case caso) {
		this.caso = caso;
	}
	
	public Value getValue() {
		return value;
	}

	public void setValue(Value value) {
		this.value = value;
	}
	
	public Criterion getCriterion() {
		return criterion;
	}
	
	public void setCriterion(Criterion criterion) {
		this.criterion = criterion;
	}
	
	public UUID getID(){return ID;}
	
	public MethodApplication(UUID ID, UUID crtID, UUID casID, UUID metID, UUID valID, UUID parentMetID, UUID bcID, Date createdDate) throws DAOException
	{
		criterion = SessionDAO.getInstance().getCriterionByID(crtID);
		caso = SessionDAO.getInstance().getCaseByID(casID);
		method = SessionDAO.getInstance().getMethodByID(metID);
		value = SessionDAO.getInstance().getValueByID(valID);
		this.bc = SessionDAO.getInstance().getLoadedBC(bcID);
		this.createdDate = createdDate;
		this.parentMetID = parentMetID;
		
		this.ID = ID;
	}
	
	public static Value getRelatedValue(UUID crtID, UUID casID, List<MethodApplication> methodApplications) throws FormulaException, DAOException
	{
		Value retVal = null;
		
		for(MethodApplication metApp : methodApplications)
			if(metApp.getCaso() != null && metApp.getCaso().getID().equals(casID) && metApp.getCriterion() != null && metApp.getCriterion().getID().equals(crtID))
			{
				retVal = metApp.getValue();
				if(metApp.getMethod() != null && metApp.getMethod().getType() == MethodType.CALCULATION)
				{
					CalculationMethod calMet = (CalculationMethod)metApp.getMethod();
					if(calMet.getFormula() != null)
						retVal = ((CalculationMethod)metApp.getMethod()).refreshValue(metApp.getCaso());
					else 
						retVal = metApp.getValue();
				}	
				
				break;
			}
		
		return retVal;
	}
	
	public static List<Criterion> getRelatedCriteria(List<MethodApplication> methodApplications)
	{
		LinkedSet<Criterion> criteria = new LinkedSet<Criterion>();
		
		for(MethodApplication metApp : methodApplications)
			criteria.add(metApp.getCriterion());
		
		return criteria;
	}
	
	public static List<Criterion> getRelatedCriterion(UUID metID, List<MethodApplication> methodApplications)
	{
		List<Criterion> retVal = new LinkedSet<Criterion>();
		
		for(MethodApplication metApp : methodApplications)
			if(metApp.getMethod().getID().equals(metID))
				retVal.add(metApp.getCriterion());
		
		return retVal;
	}
	
	public static List<CalculationMethod> getCaculationMethodsByParameter(Criterion parameter, List<MethodApplication> methodApplications)
	{
		LinkedSet<CalculationMethod> relatedMethods = new LinkedSet<CalculationMethod>();
		
		for(MethodApplication metApp : methodApplications)
		{
			Method met = metApp.getMethod(); 
			if(met != null && met.getType() == MethodType.CALCULATION)
				for(Criterion crt : met.getParameters())
					if(crt.equals(parameter))
					{
						relatedMethods.add((CalculationMethod)met);
						break;
					}
		}
		
		return relatedMethods;
	}
	
	public boolean updateItem(MethodApplication updatedItem)
	{
		boolean retVal = false;
		
		if((updatedItem.getCaso() != null && getCaso() != null) && (!updatedItem.getCaso().getID().equals(getCaso().getID())))
		{
			caso = SessionDAO.getInstance().getCaseByID(updatedItem.getCaso().getID());
			if(caso == null)
				caso = updatedItem.getCaso();
			
			retVal = true;
		}
		
		if((updatedItem.getValue() != null && getValue() != null) && (!updatedItem.getValue().getID().equals(getValue().getID())))
		{
			value = SessionDAO.getInstance().getValueByID(updatedItem.getValue().getID());
			if(value == null)
				value = updatedItem.getValue();
			
			retVal = true;
		}
		
		if((updatedItem.getMethod() != null && getMethod() != null) && (!updatedItem.getMethod().getID().equals(getMethod().getID())))
		{
			method = SessionDAO.getInstance().getMethodByID(updatedItem.getMethod().getID());
			if(method == null)
				method = updatedItem.getMethod();
			
			retVal = true;
		}
		
		if((updatedItem.getCriterion() != null && getCriterion() != null) && (!updatedItem.getCriterion().getID().equals(getCriterion().getID())))
		{
			criterion = SessionDAO.getInstance().getCriterionByID(updatedItem.getCriterion().getID());
			if(criterion == null)
				criterion = updatedItem.getCriterion();
			
			retVal = true;
		}
		
		return retVal;
	}
}
